import pyglet
import sys,os
import glob
from Tkinter import *
import tkFileDialog
from pyglet.window import key


class icuControl:
    """icuControl is an abstract class that will be the base for all controllers.
       It will work with the interface functions defined in mode, which will in
       turn work with the interface functions in view.
       One controller may support a lot of our different modes, and at this point
       I'm not sure whether we should merge this class into the base mode class,
       or keep it separate for clarification purposes, at the cost of encapsulation.
       Work on widgets will likely resolve the structure of these low level classes."""

    def __init__(self,mode):
        self.mode = mode

    ###default event handlers. if an event should be called on lower mode controllers next (lower modes also accept commands --- rare), return True.
    def on_activate(self): return False
    def on_close(self): return False
    def on_context_lost(self): return False
    def on_context_state_lost(self): return False
    def on_deactivate(self): return False
    def on_draw(self): return False
    def on_expose(self): return False
    def on_hide(self): return False
    def on_key_press(self,symbol,modifiers): return False
    def on_key_release(self,symbol,modifiers): return False
    def on_mouse_drag(self,x,y,dx,dy,buttons,modifiers): return False
    def on_mouse_enter(self,x,y): return False
    def on_mouse_leave(self,x,y): return False
    def on_mouse_motion(self,x,y,dx,dy): return False
    def on_mouse_press(self,x,y,button,modifiers): return False
    def on_mouse_release(self,x,y,button,modifiers): return False
    def on_mouse_scroll(self,x,y,scroll_x,scroll_y): return False
    def on_move(self,x,y): return False
    def on_resize(self,width,height): return False
    def on_show(self): return False
    def on_text(self,text): return False
    def on_text_motion(self,motion): return False
    def on_text_motion_select(self,motion): return False


class icuControlUL(icuControl):
    def on_activate(self): return True
    def on_draw(self): return True
    def on_move(self,x,y): return True
    def on_resize(self,width,height): return True
    
    
class icuView:
    """formerly display. icuView is an abstract base class for all mode graphics views.
       It will have an interface for mode to apply changes to widget structures,
       in addition to more traditional animated sprites (like semi-transparent overlays).
       The purpose of this class is really just to pull some code out of the overstretched
       mode class, and clarify implementation details."""

    def __init__(self,mode):
        self.closinganim = False
        self.mode = mode
        self.sprite = []
        self.rootnode = None

    def update(self,dt):
        for spr in self.sprite: spr.update(dt)
        if self.rootnode: self.rootnode.update(dt)
        if self.closinganim and not self.rootnode.sanim.active():
            for spr in self.sprite: spr.delete()
            self.rootnode.deleteheirarchy()
            main.mode.pop()

    def draw(self):
        for spr in self.sprite: spr.draw()
        if self.rootnode: self.rootnode.draw()
        
    
class icuMode:
    """icuMode is the base class for all states of the image viewer.
       Whenever an event is handled, it will more than likely change the state
       of the viewer (accessed by main.mode.append(new_Mode()), etc).
       Interface functions will handle clear cases (like left_click(x,y)), extracted
       from the obtuse controller handlers passed from window.
       In other words, the event will go from window to controller, be "decoded",
       and then handled clearly in this class, which will affect the view."""

    def __init__(self):
        self.control = icuControl(self)
        self.view = icuView(self)

    def upperlayer(self): return True

    def update(self,dt): self.view.update(dt)

    def draw(self): self.view.draw()


class icuWindow:
    """icuWindow is the concrete class that manages the pyglet window.
       Through an amazing setattr trick, it reroutes all event handlers of the window
       to the main manager, so that they can be passed to the appropriate controller.
       It's fairly nebulous at this point, but work on widgets and sprites should resolve
       a lot of the structure of this class."""

    def __init__(self,manager,*args,**kwargs):
        self.manager = manager #we'll probably need to break encapsulation to support drawing
        self.window = pyglet.window.Window(*args,**kwargs)
        self.window.set_minimum_size(50,50) #prevent the buffer scrambling that happens at low resolutions
        self.size = self.window.get_size() #used with fullscreen toggle (to revert to earlier size)

        self.setuphandlers()

    def setuphandlers(self):
        #self.window.on_draw = self.manager.on_draw
        #self.window.on_mouse_press = self.manager.on_mouse_press

        self.window.on_activate = self.manager.on_activate
        #self.window.on_close = self.manager.on_close #don't overwrite, or the [X] won't close the window.
        self.window.on_context_lost = self.manager.on_context_lost
        self.window.on_context_state_lost = self.manager.on_context_state_lost
        self.window.on_deactivate = self.manager.on_deactivate
        self.window.on_draw = self.manager.on_draw
        self.window.on_expose = self.manager.on_expose
        self.window.on_hide = self.manager.on_hide
        self.window.on_key_press = self.manager.on_key_press
        self.window.on_key_release = self.manager.on_key_release
        self.window.on_mouse_drag = self.manager.on_mouse_drag
        self.window.on_mouse_enter = self.manager.on_mouse_enter
        self.window.on_mouse_leave = self.manager.on_mouse_leave
        self.window.on_mouse_motion = self.manager.on_mouse_motion
        self.window.on_mouse_press = self.manager.on_mouse_press
        self.window.on_mouse_release = self.manager.on_mouse_release
        self.window.on_mouse_scroll = self.manager.on_mouse_scroll
        self.window.on_move = self.manager.on_move
        #self.window.on_resize = self.manager.on_resize #this is broken. nothing will draw!
        @self.window.event #on_resize needs to be added to the stack, as it has a predefined (undocumented...) function.
        def on_resize(width,height): self.manager.on_resize(width,height)
        self.window.on_show = self.manager.on_show
        self.window.on_text = self.manager.on_text
        self.window.on_text_motion = self.manager.on_text_motion
        self.window.on_text_motion_select = self.manager.on_text_motion_select
        """for evt in pyglet.window.Window.event_types:
            if evt != "on_close":
                setattr(self.window,evt,getattr(self.manager,evt)) #reroute all event handlers to the main manager"""
        """def on_key_press(symbol,modifiers): #this should be in a Controller, but for testing purposes...
            if symbol == key.F4:
                self.togglefullscreen()
        self.window.on_key_press = on_key_press #overwrite to remove esc quit functionality

        @self.window.event#.on_draw = on_draw
        def on_draw(): self.manager.draw()"""


    def togglefullscreen(self):
        self.window.set_fullscreen(not self.window.fullscreen)


class icuMain:
    """This class is the main monolith/manager/evil overlord of our program.
       It will handle preferences, the initialization of the application,
       and anything else that doesn't fit into a particular mode.
       Once again, it's nebulous now what the extent of this class will be,
       beyond basic window management and update handling."""

    def __init__(self, init_list):
        ###load preferences from a .ini file (may want to make a Preferences class and put it in self.pref)
        self.width = 640
        self.height = 480
        self.rootwin = Tk()
        self.rootwin.withdraw()
        self.curdir = os.getcwd()
        self.curCacheIndex = 0
        self.curIndex = 0
        self.piclist = []
        self.cache = [None for x in xrange(3)]
        if len(init_list) == 2:
            self.curdir = init_list[1]

        ###setup window
        self.winman = icuWindow(self,self.width,self.height,resizable=True)

        ###setup mode stack
        self.mode = []

        self.loadDir(self.curdir)
        
        pyglet.clock.schedule_interval(self.update,1/60.)

    def update(self,dt): #since this only deals with the graphics, I think all modes in the stack should be updated.
        for m in self.mode: m.update(dt)

    def loadDir(self, pathToLoad):
        if os.path.isdir(pathToLoad):
            self.curdir = os.path.abspath(pathToLoad)
            self.piclist = [f for f in glob.glob(os.path.join(pathToLoad,'*.*')) if
                                    f[-3:].lower() == 'jpg' or f[-3:].lower() == 'png' or
                                    f[-3:].lower() == 'gif' or f[-3:].lower() == 'bmp' or
                                    f[-4:].lower() == 'jpeg']
            if len(self.piclist) > 0:
                self.curIndex = 0
                if len(self.piclist) > 2:
                    self.cache[1] = pyglet.image.load(self.piclist[0])
                    self.cache[0] = pyglet.image.load(self.piclist[len(self.piclist)-1])
                    self.cache[2] = pyglet.image.load(self.piclist[1])
                    self.curCacheIndex = 1
                elif len(self.piclist) == 2:
                    self.cache[0] = pyglet.image.load(self.piclist[0])
                    self.cache[1] = pyglet.image.load(self.piclist[1])
                    self.curCacheIndex = 0
                else:
                    self.cache[0] = pyglet.image.load(self.piclist[0])
                    self.curCacheIndex = 0
        elif os.path.isfile(pathToLoad):
            dirname = os.path.dirname(pathToLoad)
            self.curdir = os.path.abspath(dirname)
            self.piclist = [f for f in glob.glob(os.path.join(dirname,'*.*')) if
                                    f[-3:].lower() == 'jpg' or f[-3:].lower() == 'png' or
                                    f[-3:].lower() == 'gif' or f[-3:].lower() == 'bmp' or
                                    f[-4:].lower() == 'jpeg']
            if os.path.abspath(pathToLoad) in self.piclist: # Inputted valid file is an image in the directory
                self.curIndex = self.piclist.index(os.path.abspath(pathToLoad))
            else:
                if len(self.piclist) > 0:
                    self.curIndex = 0
            if len(self.piclist) > 2:
                self.cache[1] = pyglet.image.load(self.piclist[self.curIndex])
                self.cache[0] = pyglet.image.load(self.piclist[self.curIndex-1 % len(self.piclist)])
                self.cache[2] = pyglet.image.load(self.piclist[self.curIndex+1 % len(self.piclist)])
                self.curCacheIndex = 1
            elif len(self.piclist) == 2:
                self.cache[0] = pyglet.image.load(self.piclist[self.curIndex])
                self.cache[1] = pyglet.image.load(self.piclist[self.curIndex+1 % len(self.piclist)])
                self.curCacheIndex = 0
            else:
                self.cache[0] = pyglet.image.load(self.piclist[self.curIndex])
                self.curCacheIndex = 0
        else:
            dirname = os.path.dirname(pathToLoad)
            if os.path.isdir(dirname):
                self.curdir = os.path.abspath(dirname)
                self.piclist = [f for f in glob.glob(os.path.join(dirname,'*.*')) if
                                        f[-3:].lower() == 'jpg' or f[-3:].lower() == 'png' or
                                        f[-3:].lower() == 'gif' or f[-3:].lower() == 'bmp' or
                                        f[-4:].lower() == 'jpeg']
                if len(self.piclist) > 0:
                    self.curIndex = 0
                    if len(self.piclist) > 2:
                        self.cache[1] = pyglet.image.load(self.piclist[0])
                        self.cache[0] = pyglet.image.load(self.piclist[len(self.piclist)-1])
                        self.cache[2] = pyglet.image.load(self.piclist[1])
                        self.curCacheIndex = 1
                    elif len(self.piclist) == 2:
                        self.cache[0] = pyglet.image.load(self.piclist[0])
                        self.cache[1] = pyglet.image.load(self.piclist[1])
                        self.curCacheIndex = 0
                    else:
                        self.cache[0] = pyglet.image.load(self.piclist[0])
                    self.curCacheIndex = 0
        self.winman.window.set_caption(self.curdir)

    def findFile(self):
        filename = tkFileDialog.askopenfilename(filetypes=[('All images', ('.jpg', '.jpeg', '.png', '.gif', '.bmp')),
                                                           ('JPEG images', ('.jpg', '.jpeg')),
                                                           ('GIF images', '.gif'),
                                                           ('PNG images', '.png'),
                                                           ('Bitmap images', '.bmp')],
                                                initialdir = self.curdir,
                                                title="Choose subdirectory")
        return filename

    ###special event handlers
    def on_draw(self):
        self.winman.window.clear()
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].upperlayer(): i -= 1 #parse down the stack until a fully opaque layer is hit
        if 0 <= i < len(self.mode):
            for j in xrange(i,len(self.mode)): #draw the layers in ascending order, so transparency is correct
                self.mode[j].draw()
        if len(self.mode)-1 == 1:
            self.mode[1].view.rootnode.draw()

    ###general event handlers (that pass instructions down to the modes)
    def on_activate(self):
        ###stack event handling (execute the controller code, and decrement and continue if the event returns True)
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_activate(): i -= 1
    def on_close(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_close(): i -= 1
    def on_context_lost(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_context_lost(): i -= 1
    def on_context_state_lost(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_context_state_lost(): i -= 1
    def on_deactivate(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_deactivate(): i -= 1
    def on_expose(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_expose(): i -= 1
    def on_hide(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_hide(): i -= 1
    def on_key_press(self,symbol,modifiers):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_key_press(symbol,modifiers): i -= 1
    def on_key_release(self,symbol,modifiers):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_key_release(symbol,modifiers): i -= 1
    def on_mouse_drag(self,x,y,dx,dy,buttons,modifiers):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_drag(x,y,dx,dy,buttons,modifiers): i -= 1
    def on_mouse_enter(self,x,y):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_enter(x,y): i -= 1
    def on_mouse_leave(self,x,y):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_leave(x,y): i -= 1
    def on_mouse_motion(self,x,y,dx,dy):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_motion(x,y,dx,dy): i -= 1
    def on_mouse_press(self,x,y,button,modifiers):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_press(x,y,button,modifiers): i -= 1
    def on_mouse_release(self,x,y,button,modifiers):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_release(x,y,button,modifiers): i -= 1
    def on_mouse_scroll(self,x,y,scroll_x,scroll_y):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_mouse_scroll(x,y,scroll_x,scroll_y): i -= 1
    def on_move(self,x,y):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_move(x,y): i -= 1
    def on_resize(self,width,height):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_resize(width,height): i -= 1
    def on_show(self):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_show(): i -= 1
    def on_text(self,text):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_text(text): i -= 1
    def on_text_motion(self,motion):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_text_motion(motion): i -= 1
    def on_text_motion_select(self,motion):
        i = len(self.mode) - 1
        while i >= 0 and self.mode[i].control.on_text_motion_select(motion): i -= 1


main = icuMain(sys.argv)

if __name__ == "__main__":
    pyglet.app.run()
